package lastmpd;

/*
 * Copyright (C) 2010 Mark Breddemann
 * 
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software Foundation;
 * either version 3 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this
 * program; if not, see <http://www.gnu.org/licenses/>.
 */

import jargs.gnu.CmdLineParser;
import jargs.gnu.CmdLineParser.IllegalOptionValueException;
import jargs.gnu.CmdLineParser.UnknownOptionException;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;

import lastmpd.lastfmAlbumCache.LastfmAlbumCache;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bff.javampd.objects.MPDSong;

public class LastMpd {

	private final Log log = LogFactory.getLog(this.getClass());
	private MpdConnection mpdConnection;
	private LastfmConnection lastfmConnection;
	private LastfmAlbumCache lastfmAlbumCache;
	private String defaultUsername;
	private int defaultTrackCount;
	private String defaultMpdHost;
	private int defaultMpdPort;
	private String defaultMpdPassword;
	private static final String VERSION = "0.1";
	
	public static void main(String[] args) {
		new LastMpd(args);
	}
	
	public LastMpd(String[] args) {
		try {
			
			Properties properties = new Properties();
			properties.load(new FileInputStream("lastmpd.properties"));

			defaultUsername = properties.getProperty("lastfm.default.username");
			defaultTrackCount = Integer.parseInt(properties.getProperty("lastfm.default.trackCount", "0"));
			defaultMpdHost = properties.getProperty("mpd.host");
			defaultMpdPort = Integer.parseInt(properties.getProperty("mpd.port", "6600"));
			defaultMpdPassword = properties.getProperty("mpd.password");
			
			if (args.length == 0 || "".equals(args[0])) {
				printUsage();
				return;
			}
			
			lastfmConnection = new LastfmConnection();
			lastfmAlbumCache = new LastfmAlbumCache(properties, lastfmConnection);
			
			CmdLineParser parser = new CmdLineParser();
			CmdLineParser.Option usernameParser = parser.addStringOption('u', "user");
			CmdLineParser.Option trackCountParser = parser.addIntegerOption('c', "count");
			CmdLineParser.Option artistCountParser = parser.addIntegerOption('s', "similarArtistCount");
			CmdLineParser.Option artistParser = parser.addStringOption('a', "artist");
			CmdLineParser.Option mpdHostParser = parser.addStringOption("mpdHost");
			CmdLineParser.Option mpdPortParser = parser.addIntegerOption("mpdPort");
			CmdLineParser.Option mpdPasswordParser = parser.addStringOption("mpdPassword");
			
			String[] options = new String[args.length - 1];
			System.arraycopy(args, 1, options, 0, args.length - 1);
			parser.parse(options);
			
			String username = (String)parser.getOptionValue(usernameParser, defaultUsername);
			String artist = (String)parser.getOptionValue(artistParser);
			int count = (Integer)parser.getOptionValue(trackCountParser, defaultTrackCount);
			int artistCount = (Integer)parser.getOptionValue(artistCountParser, 0);
			String mpdHost = (String)parser.getOptionValue(mpdHostParser, defaultMpdHost);
			int mpdPort = (Integer)parser.getOptionValue(mpdPortParser, defaultMpdPort);
			String mpdPassword = (String)parser.getOptionValue(mpdPasswordParser, defaultMpdPassword);

			mpdConnection = new MpdConnection(mpdHost, mpdPort, mpdPassword);
			
			
			
			if (args[0].equalsIgnoreCase("top")) {
				addTopTracks(artist, count);
			} else if (args[0].equalsIgnoreCase("topsimilar")) {
				addTopSimilar(artist, artistCount, count);
			} else if (args[0].equalsIgnoreCase("usertop")) {
				addTopUserTracks(username, count);
			} else if (args[0].equalsIgnoreCase("userloved")) {
				addLovedTracks(username, count);
			}
			
			
		} catch (FileNotFoundException e) {
			log.error(e.getMessage());
		} catch (IOException e) {
			log.error(e.getMessage());
		} catch (MpdException e) {
			log.error(e.getMessage());
		} catch (IllegalOptionValueException e) {
			log.error(e.getMessage());
		} catch (UnknownOptionException e) {
			log.error(e.getMessage());
		} catch (CommandException e) {
			log.error(e.getMessage());
		}
		
	}
	
	private void addTopSimilar(String artist, int artistCount, int trackCount) throws MpdException, CommandException {
		if (artist == null) throw new CommandException("No artist given");
		if (trackCount == 0) throw new CommandException("No or invalid count");
		if (artistCount == 0) throw new CommandException("No or invalid artistCount");
		
		log.debug("adding top " + trackCount + " tracks from " + artistCount + " similar artists from " + artist);
		
		ArrayList<LastfmSong> tracks = lastfmConnection.getTopTracksFromSimilarArtists(artist, artistCount, trackCount);
		lastfmAlbumCache.fillAlbumNames(tracks);
		addSongsToPlaylist(tracks);
	}
	
	private void addTopUserTracks(String user, int trackCount) throws MpdException, CommandException {
		if (user == null) throw new CommandException("No user given");
		if (trackCount == 0) throw new CommandException("No or invalid count");
		
		log.debug("adding " + trackCount + " top tracks from user " + user);
		
		ArrayList<LastfmSong> tracks = lastfmConnection.getTopTracksFromUser(user, trackCount);
		lastfmAlbumCache.fillAlbumNames(tracks);
		addSongsToPlaylist(tracks);
	}
	
	private void addTopTracks(String artist, int count) throws MpdException, CommandException {
		if (artist == null) throw new CommandException("No artist given");
		if (count == 0) throw new CommandException("No or invalid count");
		
		log.debug("adding top " + count + " tracks from " + artist);
		
		ArrayList<LastfmSong> tracks = lastfmConnection.getTopTracksFromArtist(artist, count);
		lastfmAlbumCache.fillAlbumNames(tracks);
		addSongsToPlaylist(tracks);
	}
	
	private void addLovedTracks(String user, int count) throws MpdException, CommandException {
		if (user == null) throw new CommandException("No user given");
		if (count == 0) throw new CommandException("No or invalid count");
		
		log.debug("adding " + count + " loved tracks from " + user);
		
		ArrayList<LastfmSong> tracks = lastfmConnection.getLovedTracks(user, count);
		lastfmAlbumCache.fillAlbumNames(tracks);
		addSongsToPlaylist(tracks);
	}
	
	private void addSongsToPlaylist(ArrayList<LastfmSong> songs) throws MpdException {
		for (LastfmSong song:songs) {
			MPDSong mpdSong = mpdConnection.findSong(song.getArtist(), song.getSong(), song.getAlbum());
			if (mpdSong != null) {
				mpdConnection.addSongToPlaylist(mpdSong);
			}
		}
	}
	
	private void printUsage() {
		System.out.println("lastMPD version " + VERSION);
		System.out.println("");
		System.out.println("USAGE: lastmpd.sh command [{-a,--artist} artist] [{-c,--trackCount} count] [{-u, --user} username] [{-s,--similarArtistCount} count]");
		System.out.println();
		System.out.println("Available commands:");
		System.out.println(" top\t\tadds top tracks from the given artist to the playlist");
		System.out.println(" topSimilar\tadd top songs from similar artists");
		System.out.println(" userTop\tadd the most played songs from a user");
		System.out.println(" userLoved\t add up to 50 loved songs from a user");
		System.out.println("");
		System.out.println("Default values:");
		System.out.println(" username=" + defaultUsername);
		System.out.println(" trackCount=" + defaultTrackCount);
		System.out.println(" mpdHost=" + defaultMpdHost);
		System.out.println(" mpdPort=" + defaultMpdPort);
		System.out.println(" mpdPassword=" + (defaultMpdPassword!=null || !"".equals(defaultMpdPassword)?"***":""));
	}

	class CommandException extends Exception {
		private static final long serialVersionUID = 0L;

		public CommandException(String message, Throwable cause) {
			super(message, cause);
		}
		public CommandException(String message) {
			super(message);
		}

		public CommandException(Throwable cause) {
			super(cause);
		}
	}
}
